//
//  Polygon.m
//  Coffee
//
//  Created by sabhanayagam b on 5/27/11.
//  Copyright 2011 Cognizant. All rights reserved.
//

#import "Polygon.h"

@implementation Polygon
@synthesize points , initialPoints;
@synthesize  center , offset , axes , edge , count , dotProduct , size;
@synthesize collisionResponse , objectDiff , _center;

-(void) dealloc
{
	free(points);
	[edge release];	
	[axes release];
	[collisionResponse release];
	[super dealloc];
}

//Constructor
-(id) initWithvertices:(CGPoint*)pts count:(int)cnt size:(CGSize)wd
{
	if ((self = [super init])) {
		points = malloc(sizeof(CGPoint)*cnt);
		initialPoints = malloc(sizeof(CGPoint)*cnt);
		count  = cnt;
		//Deep copy all the vertex data into Shape vertices
		for (int i = 0; i< count; i++) {
			points[i] = pts[i];
			initialPoints[i] = pts[i];
		}
		size   = wd;
		edge   = [[Vector2D alloc] initWithComponentX:0 Y:0];
		axes   =  [[NSMutableArray alloc] init];  
		objectDiff = [Vector2D createVector];
		dotProduct = 0.0f;
	}
	
	return self;
}


/**
 * Calculate the Size of the polygon.
 */

-(void) defineSize
{
	if (size.width !=0 && size.height != 0) {
		for (int i = 0; i< count; i++) 
		{
			points[i].x *= size.width;
			points[i].y *= size.height;
			//NSLog(@"x : %f y : %f",points[i].x , points[i].y);
			initialPoints[i].x *= size.width;
			initialPoints[i].y *= size.height;
			
		}
	}
	[self defineCenter];
	[self defineAxes];
}

/**
 * Calculate the Center of the polygon.
 */
-(void) defineCenter
{
	CGFloat totalX = 0;
	CGFloat totalY = 0;
	
	for (int i = 0; i< count; i++) 
	{
		CGPoint p = points[i];
		totalX += p.x;
		totalY += p.y;
	}
	
	center.x = totalX / count;
	center.y = totalY / count;
	
	_center = center;
	
}
/**
 * Establishes an offset that is reflected in the positioning
 * of the vertices and the registration point by which rotation
 * is applied.
 */
-(void) setOffset:(CGPoint) value
{
	offset = value;
	
	// The center as well as all of the vertices need to be
	// accommodated.
	center.x -= value.x;
	center.y -= value.y;
	
	for (int i = 0; i< count; i++) 
	{
		CGPoint pt = points[i];
		pt.x -= value.x;
		pt.y -= value.y;
		points[i] = pt;
	}
	
}

/**
 * Iterates over all the points and calculates the axes 
 * based on their edges.
 */
-(void) defineAxes
{	
	//NSLog(@"points count %d" ,count);
	
	// clear the axes vector.
	for(int i=0; i < count; i++)
	{
		// if there's another point available, define an edge.
		
		//CGPoint p1 = points[i]; 
		//CGPoint p2 = points[(i + 1 == count ? 0 : i + 1)]; 
		
		// if there's another point available, define an edge.
		if((i + 1) < count)
		{
			edge.vx = points[i].x - points[i + 1].x;
			edge.vy = points[i].y - points[i + 1].y;
		}
		else // otherwise, connect the edge to the first point.
		{
			edge.vx = points[i].x - points[0].x;
			edge.vy = points[i].y - points[0].y;
		}
		//NSLog(@"axes vx vy %f %f" , edge.vx , edge.vy);

		
		
		//edge.vx = p1.x - p2.x;
		//edge.vy = p1.y - p2.y;
	
		// generate potential axis which is perpendicular to the edge
		// and make sure it's been normalized.
		Vector2D *potentialAxis = [[edge rightNormal]normalize];
		
		// Round the axis member's float values to the nearest 2 decimal places.
		// This ensures we catch very similar axes that are safe to bipass.
		potentialAxis.vx = (int)(potentialAxis.vx * 100.0f)/100.0f;
		potentialAxis.vy = (int)(potentialAxis.vy * 100.0f)/100.0f;
		//NSLog(@"axes count %f %f" , potentialAxis.vx , potentialAxis.vy);

		[axes addObject:potentialAxis];
	}
	//removeDuplicateAxes(_axes);
	//NSLog(@"axes count %d" , [axes count]);
}

/**
 * Moves the polygon by the amount defined in the velocity
 * Vector2D instance.
 */
-(void) translateVelocity:(Vector2D*)velocity
{	
	// The center as well as all of the vertices need to be
	// accommodated.
	center.x += velocity.vx;
	center.y += velocity.vy;
	
	for(int i=0; i < count; i++)
	{
		points[i].x += velocity.vx;
		points[i].y += velocity.vy;
		
		////NSLog(@"velocity %f %f",points[i].x , points[i].y);
	}
}

/**
 * Moves the polygon by the amount defined in the velocity
 * Vector2D instance.
 */
-(void) restInPeace
{	
	
	//center.x = _center.x;
	//center.y = 8.0f;
	// The center as well as all of the vertices need to be
	// accommodated.
	for(int i=0; i < count; i++)
	{
		points[i].x = points[i].x;
		points[i].y = initialPoints[i].y;
		
		NSLog(@"velocity %f %f",initialPoints[i].x , initialPoints[i].y);
	}
	[self defineCenter];
}



/**
 * Checks whether or not this polygon is colliding with another. If it is, a boolean
 * value is returned and a translation vector is defined to allow for calling object
 * to be moved out of the collision-space.
 */
-(BOOL) isCollidingWith:(Polygon*)polygon translationVector:(Vector2D*)translationVector
{
	// request that a collision response be invoked on the argument polygon.
	collisionResponse = [self invokeCollisionResponse:polygon];
	// if a collision response exists and it contains an amount worth addressing, do so.
	if(collisionResponse != nil && collisionResponse.amount > 0.0f)
	{	
		// calculate the distance between the colliding polygons.
		
		objectDiff.vx = center.x - polygon.center.x;
		objectDiff.vy = center.y - polygon.center.y;
		
		// calculate the dot product of the distance and active axis.
		dotProduct = [objectDiff dotProduct:collisionResponse.axis];
		
		// if the dot product is less than zero, invert the responding vector. 
		if (dotProduct < 0)
		{
			collisionResponse.axis.vx *= -1;
			collisionResponse.axis.vy *= -1;
		}
		
		// Finally, make sure the translation vector being passed in has the data it
		// need to be useful in moving this polygon out of the colliding region.
		translationVector.vx = collisionResponse.axis.vx * collisionResponse.amount;
		translationVector.vy = collisionResponse.axis.vy * collisionResponse.amount;
		
		return YES;
	}
	else
	{
		return NO;
	}
	
}

/**
 * Discovers whether or not a collision with the argument polygon exists.
 * If so, a CollisionResponse object is returned which contains the amount
 * of penetration and a directional vector.
 */
-(CollisionResponse*) invokeCollisionResponse:(Polygon*)polygon
{
	float amount;
	float smallestAmount = 9999.999f;
	Vector2D *smallestAmountAxis;
	
	// TODO: find out which axis separates the two objects and start there for
	// potential performance increase.
	
	// iterate over all of these axes and check to see if the projected points
	// overlap.
	for(Vector2D *axis in axes)
	{
		amount = [self getAxisOverlap:polygon Axis:axis];
		
		// If the overlap is greater than zero, check to see if it's the 
		// smallest value of the group.
		if(amount == 0) return nil;
		else if(amount < smallestAmount)
		{
			smallestAmount = amount;
			smallestAmountAxis = axis;
		}
	}
	
	// iterate over all of the argument polygon axes and check to see if the 
	// projected points overlap.
	for(Vector2D *axis in polygon.axes)
	{
		amount = [self getAxisOverlap:polygon Axis:axis];
		
		// If the overlap is greater than zero, check to see if it's the 
		// smallest value of the group.
		if(amount == 0) return nil;
		else if(amount < smallestAmount)
		{
			smallestAmount = amount;
			smallestAmountAxis = axis;
		}
	}
	
	
	CollisionResponse *colresp = [[CollisionResponse alloc] initWithInfo:smallestAmountAxis amount:smallestAmount];
	
	return colresp;
}
/**
 * Calls for all vertices represented on both this and the argument polygon
 * to be projected on a given axis.
 * 
 * Returns the amount of overlap that exists.
 */
-(float) getAxisOverlap:(Polygon*)polygon Axis:(Vector2D*)axis
{
	
	//Project all the points to the calculated Axis
	NSMutableArray *polyAprojectedPoints = [[[NSMutableArray alloc]init] autorelease];
	NSMutableArray *polyBprojectedPoints = [[[NSMutableArray alloc]init] autorelease];
	
	//Polygon A Project all the points of THIS to the Axis
	for (int a = 0; a < count; a++) 
	{
		Vector2D *vect2 = [[[Vector2D alloc] initWithComponentX:points[a].x Y:points[a].y] autorelease];
		float dotproduct = [axis dotProduct:vect2];
		//NSLog(@"Point A %f %f Vect  A %f %f dotproduct %f axis %f %f",points[a].x , points[a].y,vect2.vx , vect2.vy , dotproduct,axis.vx,axis.vy);

		[polyAprojectedPoints addObject:[NSNumber numberWithFloat:dotproduct]];
	}
	//NSLog(@"\n");
	//Polygon B Project all the points of ARGUMENT to the Axis
	for (int b = 0; b < polygon.count; b++) 
	{
		
		////NSLog(@"B %f %f",polygon.points[b].x , polygon.points[b].y);
		Vector2D *vect2 = [[[Vector2D alloc] initWithComponentX:polygon.points[b].x Y:polygon.points[b].y] autorelease];
		////NSLog(@"vect B %f %f",vect2.vx , vect2.vy);
		float dotproduct = [axis dotProduct:vect2];
		[polyBprojectedPoints addObject:[NSNumber numberWithFloat:dotproduct]];
	}
	// find the min and max scalar values of each object.
	float thisMin = [[polyAprojectedPoints objectAtIndex:0] floatValue];
	float thisMax = [[polyAprojectedPoints objectAtIndex:0] floatValue];
	
	for (int i = 0; i < [polyAprojectedPoints count]; i++) 
	{
		float scalar = [[polyAprojectedPoints objectAtIndex:i] floatValue];
		////NSLog(@"poly A projectedPoints %f",scalar);
		if(scalar < thisMin)thisMin = scalar;
		if(scalar > thisMax)thisMax = scalar;
		
	}
	
	// find the min and max scalar values of each object.
	float thatMin = [[polyBprojectedPoints objectAtIndex:0] floatValue];
	float thatMax = [[polyBprojectedPoints objectAtIndex:0] floatValue];
	for (int i = 0; i < [polyBprojectedPoints count]; i++) 
	{
		float scalar = [[polyBprojectedPoints objectAtIndex:i] floatValue];
		////NSLog(@"poly B projectedPoints %f",scalar);
		if(scalar < thatMin) thatMin = scalar;
		if(scalar > thatMax) thatMax = scalar;
	}
	float overlapAmount = 0.0f;
	// if there is any overlap between the objects 
	// (min of tile rect is less than max of player rect)
	// then there is a collision present along the axis.
	if((thatMin <= thisMax) && (thatMax >= thisMax))
		overlapAmount = thisMax - thatMin;
	else if((thisMin <= thatMax) && (thisMax >= thatMax))
		overlapAmount = thatMax - thisMin;
	
	return overlapAmount;
}
@end
